package core;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;

import tasks.ParseTorrent;

public class TorrentManager {

    private static TorrentManager instance = new TorrentManager();
    private HashMap<byte[], ArrayList<Peer>> dico;
    private ArrayList<Torrent> torrents;

    private TorrentManager() {
        this.torrents = new ArrayList<Torrent>();
        this.dico = new HashMap<byte[], ArrayList<Peer>>();

    }

    public static TorrentManager getInstance() {
        return instance;
    }

    public Torrent addTorrent(String path) throws InvalidBEncodingException, IOException, InterruptedException, ExecutionException {
        CompletionService<Torrent> completionService = new ExecutorCompletionService<Torrent>(Executor.getThreadPool());
        completionService.submit(new ParseTorrent(path));

        Torrent torrent = completionService.take().get();
        this.dico.put(torrent.getHashInfo(), new ArrayList<Peer>());
        this.torrents.add(torrent);

        return torrent;
    }

    public Torrent addTorrent(File file) throws InvalidBEncodingException, IOException, InterruptedException, ExecutionException {
        CompletionService<Torrent> completionService = new ExecutorCompletionService<Torrent>(Executor.getThreadPool());
        completionService.submit(new ParseTorrent(file));

        Torrent torrent = completionService.take().get();
        this.dico.put(torrent.getHashInfo(), new ArrayList<Peer>());
        this.torrents.add(torrent);
        return torrent;
    }

    public Torrent find(String name) {
        int i = 0;

        while (i < this.torrents.size()) {
            if (this.torrents.get(i).getName().equals(name)) {
                return torrents.get(i);
            }

            i++;
        }

        return new Torrent();
    }

    public ArrayList<Torrent> getTorrents() {
        return torrents;
    }

    public Torrent getTorrent(byte[] hash) {
        for (int i = 0; i < this.torrents.size(); i++) {
            if (Arrays.equals(this.torrents.get(i).getHashInfo(), hash)) {
                return this.torrents.get(i);
            }
        }

        return null;
    }
    
    public Torrent getTorrentByName(String name) {
        for (int i = 0; i < this.torrents.size(); i++) {
            if (this.torrents.get(i).getName().equals(name)) {
                return this.torrents.get(i);
            }
        }

        return null;
    }
    
    public void removeTorrent(String name) {
    	Torrent torrent = this.getTorrentByName(name);
    	
    	if(torrent != null) {
	    	byte[] infoHash = torrent.getHashInfo();
	    	
	    	ArrayList<Peer> peerList = this.dico.get(infoHash);
	    	this.dico.remove(infoHash);
	    	this.torrents.remove(torrent);
	    	
	    	for(int i=0; i<peerList.size(); i++) {
	    		peerList.get(i).close();
	    	}
    	}
    }

    public ArrayList<Peer> getPeersByTorrent(byte[] hash) {
        return this.dico.get(hash);
    }

    public void addPeerByTorrent(byte[] hash, Peer peer) {
        ArrayList<Peer> old_list = this.dico.get(hash);
        ArrayList<Peer> new_List;
        if (old_list != null) {
            new_List = new ArrayList<Peer>(old_list);
        } else {
            new_List = new ArrayList<Peer>();
        }
        new_List.add(peer);
        this.dico.put(hash, new_List);
    }
    
    public void addPeersByTorrent(byte[] hash, ArrayList<Peer> peers) {
        ArrayList<Peer> old_list = this.dico.get(hash);
        ArrayList<Peer> new_List;
        if (old_list != null) {
            new_List = new ArrayList<Peer>(old_list);
        } else {
            new_List = new ArrayList<Peer>();
        }
        new_List.addAll(peers);
        this.dico.put(hash, new_List);    	
    }
    
    public void removePeerByTorrent(byte[] hash, Peer peer){
        
        ArrayList<Peer> old_list = this.dico.get(hash);

        old_list.remove(peer);
        this.dico.put(hash, old_list);
        
    }
}
